<?php
error_reporting(E_ERROR | E_PARSE);
set_time_limit(0);
$act = $_REQUEST['a'];
if ($act) $act();
else init();

function submit()
{
    ob_end_flush();
    ob_implicit_flush();
    
    $post = $_POST;
    if ($post['id'] == '')
        die('请填写完整信息');
    
    echo '<style>body { font-size: 14px; }</style>';
    echo '开始同步：<br/>';
    
    $c      = $post['check'];
    $s      = config();
    $config = $s[$post['id']];
    $dir    = $config['git'];
    
    foreach ($post['op'] as $op) {
        $server = $config['rows'][$op];
        $host   = explode(':', $server['host']);
        $conn   = ssh2_connect($host[0], $host[1] ?? '22');
        if (!$conn) {
            echo $server['name'] . '：远程连接失败。<br/><br/>';
        }
        elseif (
            ($server['pass'] && ssh2_auth_password($conn, $server['user'], $server['pass']))
            ||
            ($server['key'] && ssh2_auth_pubkey_file($conn, $server['user'], $server['pub'], $server['key']))
        ) {
            $sftp  = ssh2_sftp($conn);
            $paths = [];
            
            foreach ($post['files'] as $file) {
                $loc = rtrim($dir, "\/\\") . DIRECTORY_SEPARATOR . $file;
                $rem = $server['web'] . '/' . $file;
                
                // check
                if ($c) {
                    echo $loc . (is_file($loc) ? ': Check OK' : ': Check Err') . '，';
                    echo $rem . (ssh2_exec($conn, 'ls ' . $rem) ? ': Check OK' : ': Check Err') . '<br/>';
                }
                else {
                    $path = dirname($rem);
                    $m    = ($path);
                    if (!in_array($m, $paths)) {
                        $paths[] = $m;
                        ssh2_sftp_mkdir($sftp, $path, 0755, true);
                    }
                    
                    if (ssh2_scp_send($conn, $loc, $rem, 0644))
                        echo $file . ' -> ' . $rem, '<br/>';
                    else
                        echo $file . ' : Error.<br/>';
                }
            }
            
            echo $server['name'] . '：同步完成。<br/><br/>';
            echo '<script>setTimeout(function(){ parent.$("#log").addClass("hide"); }, 9000);</script>';
        }
        else {
            echo $server['name'] . '：远程连接 认证失败。<br/><br/>';
        }
        
        unset($sftp);
        ssh2_disconnect($conn);
    }
}

function git()
{
    // git log -15 --date=format:"%Y-%m-%d %H:%M:%S" --pretty=format:"%H %cd %s" --name-status
    // git diff head~0 head~10 --name-only
    
    $post = $_POST;
    if ($post['id'] == '')
        json(1, '请填写完整信息');
    
    $s      = config();
    $config = $s[$post['id']];
    
    $num  = 5;
    $cmd  = 'git -C "' . $config['git'] . '" log -' . $num . ' --date=format:"%m-%d %H:%M:%S" --pretty=format:"-Hash:%h %cd %s" --name-status';
    $list = ex($cmd);
    
    $data = [];
    foreach ($list as $line) {
        // -Hash:b1b0e82 04-19 17:42:24 1、ig 2、忽略
        $line = trim($line);
        if (strpos($line, 'Hash:') == 1) {
            if (!empty($row))
                $data[] = $row;
            
            $row         = [];
            $row['hash'] = substr($line, 6, 7);
            $row['time'] = substr($line, 14, 14);
            $row['text'] = substr($line, 29);
        }
        elseif (!empty($line)) {
            $row['files'][] = trim(substr($line, 1));
        }
        
    }
    if (!empty($row))
        $data[] = $row;
    
    json(0, $data);
}

function delete()
{
    $config = config();
    if ($_POST['id'] == '')
        json(1, '请填写完整信息');
    
    unset($config[$_POST['id']]);
    config($config);
    
    json();
}

function addweb()
{
    $config = config();
    $post   = $_POST;
    if ($post['id'] == '' || !$post['name'] || !$post['host'] || !$post['user'] || (!$post['pass'] && !$post['key']) || !$post['web']) {
        json(1, '请填写完整信息');
    }
    if (!$config[$post['id']]['rows'])
        $config[$post['id']]['rows'] = [];
    
    $config[$post['id']]['rows'][] = $post;
    config($config);
    
    json();
}

function add()
{
    $config = config();
    
    $config[] = $_POST;
    
    config($config);
    
    json();
}

function json($flag = 0, $data = null)
{
    $ret = ['err' => $flag];
    if (is_string($data))
        $ret['msg'] = $data;
    elseif (is_array($data))
        $ret['ret'] = $data;
    
    die(json_encode($ret));
}

function init()
{
    $config = config();
    $tr     = '';
    foreach ($config as $id => $t) {
        $tr .= '<tr>';
        $tr .= '<td id="' . $id . '" class="one"><a href="">' . $t['name'] . '</a></td>';
        $tr .= '<td class="git"><select size="5" class="hide"></select><ul class="files hide"></ul></td>';
        $tr .= '<td><dl>';
        foreach ($t['rows'] as $k => $v) {
            $tr .= '<dd><label title="' . $v['host'] . chr(10) . $v['web'] . '"><input value="' . $k . '" type="checkbox" /> ' . $v['name'] . '</label></dd>';
        }
        $tr .= '</dl></td>';
        $tr .= '<td>';
        $tr .= '<a class="submit" href="">同步</a>';
        $tr .= '<a class="check" href="">检查</a>';
        $tr .= '<a class="remote" href="">添加</a>';
        $tr .= '<a class="delete" href="">删除</a>';
        $tr .= '</td>';
        $tr .= '</tr>';
    }
    $tr .= '<tfoot><tr><td colspan="4"><button id="btn">新建任务</button></td></tr></tfoot>';
    
    $html = '
<link rel="icon" type="image/x-icon" href="https://github.githubassets.com/favicon.ico">
<style>
    .hide { display: none; }
    #new { margin: 10px 0; }
    .new label { display: block; margin: 5px 0; }
    .new label span { display: inline-block; width: 120px; }
    .new label input { width: 400px; padding: 5px; }
    #tab { border-collapse: collapse; border-spacing: 0; border: 1px solid #ddd; width: 100%; }
    th { background: #eee; padding: 10px; }
    tr {}
    tr.select { background: #dff0d8; }
    tr td:first-child, tr td:last-child { text-align: center; }
    td { padding: 20px 10px; border: 1px solid #ddd; font-size: 14px; }
    td a { text-decoration: none; color: green; margin-right: 10px; }
    td a:hover { color: #ff8e4a; font-weight: bold; }
    td a:last-child { margin-right: 0; }
    select { height: 130px; width: 360px; font-size: 14px; }
    option { padding: 3px; }
    ul { list-style: none; vertical-align: top; margin: 0 10px; padding: 0; display: inline-block; font-size: 14px; }
    dl { margin: 0px; padding: 0; }
    dd { margin: 0; padding: 5px 0; }
    dd label { display: block; }
    dd input { vertical-align: middle; }
    #log { border: 1px solid #ddd; margin: 0 0 20px; }
    iframe { border: 0; margin: 0; height: 360px }
</style>
<body>
<div id="new" class="new hide">
    <form>
    <label><span>名称：</span><input name="name" placeholder="任务名称"/></label>
    <label><span>Git地址：</span><input name="git" placeholder="Git仓库地址"/></label>
    <label><button type="button" id="add">添加</button></label>
    </form>
</div>
<div id="remote" class="new hide">
    <form>
    <label><span>服务器名称：</span><input name="name" placeholder="服务器名称"/></label>
    <label><span>服务器地址：</span><input name="host" placeholder="远程服务器地址,IP:PORT"/></label>
    <label><span>服务器用户：</span><input name="user" placeholder="登录用户"/></label>
    <label><span>服务器密码：</span><input name="pass" placeholder="登录密码，和密钥只能选其一"/></label>
    <label><span>公钥地址：</span><input name="pub" placeholder="登录公钥，和密码只能选其一"/></label>
    <label><span>密钥地址：</span><input name="key" placeholder="登录密钥，和密码只能选其一"/></label>
    <label><span>项目地址：</span><input name="web" placeholder="服务器上的项目地址"/></label>
    <label><button type="button" id="addweb">添加</button></label>
    </form>
</div>
<form id="form" action="?a=submit" method="post" target="iframe" class="hide"></form>
<div id="log" class="hide">
    <iframe name="iframe" src="about:_blank" width="100%"></iframe>
</div>
<table id="tab">
    <thead>
    <tr>
        <th width="200">任务</th>
        <th width="">GIT</th>
        <th width="200">远程</th>
        <th width="200">操作</th>
    </tr>
    </thead>
    <tbody>
    ' . $tr . '
    </tbody>
</table>
<script src="https://cdn.bootcss.com/jquery/3.4.0/jquery.min.js"></script>
<script>
    $("#btn").click(function() {
        $("#new").toggle(".hide").find("form")[0].reset();
    });
    $(".remote").click(function() {
        $("#remote").toggle(".hide").find("form")[0].reset();
        return false;
    });
    $("tbody tr").each(function(i, n) {
        n.addEventListener("click", function() {
            $(this).addClass("select").siblings().removeClass("select");
        }, true);
    });

    $("#add").click(function() {
        var data = {};
        $("#new input").each(function(i, n) {
            var o = $(n), m = o.attr("name"), v = o.val();
            data[m] = v;
        });
        $.post("?a=add", data, function(ret) {
            if (ret.err == "0")
                location.reload();
            else
                alert(ret.msg);
        }, "json");
    });
    $("#addweb").click(function() {
        var data = {id:$("tr.select .one").attr("id")};
        $("#remote input").each(function(i, n) {
            var o = $(n), m = o.attr("name"), v = o.val();
            data[m] = v;
        });
        $.post("?a=addweb", data, function(ret) {
            if (ret.err == "0")
                location.reload();
            else
                alert(ret.msg);
        }, "json");
    });
    
    var data = [];
    $(".one a").click(function () {
        var o = $(this), id = o.parent().attr("id"), tr = o.parents("tr");
        $.post("?a=git", {id: id}, function (ret) {
            if (ret.err == "0"){
                data[id] = ret.ret;
                var opt = "", sel = tr.find("select"), ul = tr.find("files");
                $.each(ret.ret, function(i, n) {
                    opt += "<option value="+i+">Time："+n.time+"，说明："+n.text+"</option>";
                });
                sel.empty().append(opt).removeClass("hide");
                ul.empty();
                sel.val(0).find("option").eq(0).click();
            }
            else
                alert(ret.msg);
        }, "json");
        return false;
    });
    $("select").on("click", "option", function() {
        var o = $(this), td = o.parents("td"), tr = td.parent(),
            id = tr.find(".one").attr("id"), ul = td.find("ul"),
            i = o[0].index, fs = data[id][i].files, li = "";
        
        $.each(fs, function(j, n) {
            li += "<li>"+n+"</li>";
        });
        ul.empty().append(li).removeClass("hide");
    });
    
    function submit(is, check) {
        var o = $(is), tr = o.parents("tr"), chk = tr.find("input:checked"),
            id = tr.find(".one").attr("id"), sel = tr.find("select").val()
            
        if (chk.length == 0 || sel == ""){
            alert("请先选择项目内容")
            return false;
        }
        
        var dt = data[id][sel]["files"], form = $("#form"), ipt = "<button>提交</button>";
        if (check == 1)
            ipt += "<input type=hidden name=check value=1 />";
        ipt += "<input type=hidden name=id value="+id+" />";
        $.each(chk, function(i, n) {
            ipt += "<input type=hidden name=op[] value="+$(n).val()+" />";
        });
        $.each(dt, function(i, n) {
            ipt += "<input type=hidden name=files[] value="+n+" />";
        });
        $("#log").removeClass("hide");
        form.empty().append(ipt).submit();
    }
    $(".check").click(function() {
        submit(this, 1);
        return false;
    });
    $(".submit").click(function() {
        submit(this, 0);
        document.body.scrollTop = document.documentElement.scrollTop = 0;
        return false;
    });
    
    $(".delete").click(function() {
        if (confirm("确定要删除这个任务吗？")){
            var data = {id:$("tr.select .one").attr("id")};
            $.post("?a=delete", data, function(ret) {
            if (ret.err == "0")
                location.reload();
            else
                alert(ret.msg);
            }, "json");
        }
        return false;
    });
</script>
</body>
    ';
    
    echo $html;
}

function config($config = null)
{
    $file = './config.json';
    if ($config) {
        file_put_contents($file, json_encode($config, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
        
        return true;
    }
    else {
        $config = file_get_contents($file);
        
        return $config ? json_decode($config, true) : [];
    }
}

function ex($cmd)
{
    exec($cmd, $ret);
    
    return $ret;
}